You can’t connect the dots looking forward you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future. You have to trust in something: your gut, destiny, life, karma, whatever. Because believing that the dots will connect down the road will give you the confidence to follow your heart, even when it leads you off the well worn path.
>>> nums = [1,2,3] # note that ... varies: these are different objects >>> iter(nums) <listiterator object at ...> >>> nums.__iter__() <listiterator object at ...> >>> nums.__reversed__() <listreverseiterator object at ...>
>>> import itertools >>> defg(): ... print'--start--' ... for i in itertools.count(): ... print'--yielding %i--' % i ... try: ... ans = yield i ... except GeneratorExit: ... print'--closing--' ... raise ... except Exception as e: ... print'--yield raised %r--' % e ... else: ... print'--yield returned %s--' % ans
>>> defdecorator_with_arguments(arg): ... print"defining the decorator" ... def_decorator(function): ... # in this inner function, arg is available too ... print"doing decoration,", arg ... return function ... return _decorator >>> @decorator_with_arguments("abc") ... deffunction(): ... print"inside function" defining the decorator doing decoration, abc >>> function() inside function
>>> classdecorator_class(object): ... def__init__(self, arg): ... # this method is called in the decorator expression ... print"in decorator init,", arg ... self.arg = arg ... def__call__(self, function): ... # this method is called to do the job ... print"in decorator call,", self.arg ... return function >>> deco_instance = decorator_class('foo') in decorator init, foo >>> @deco_instance ... deffunction(*args, **kwargs): ... print"in function,", args, kwargs in decorator call, foo >>> function() in function, () {}
classassert_raises(object): # based on pytest and unittest.TestCase def__init__(self, type): self.type = type def__enter__(self): pass def__exit__(self, type, value, traceback): if type isNone: raise AssertionError('exception expected') if issubclass(type, self.type): returnTrue# swallow the expected exception raise AssertionError('wrong exception type')
在ctags的例子中,我们没告诉Portage有关任何依赖。当情况是这样时,没关系,因为ctags仅仅需要一个基本的工具链来编译和运行(参见Implicit System Dependency理解为何我们不需要显式依赖)。然而事情很少这么简单。
这是app-misc/detox/detox-1.1.1.ebuild:
# Copyright 1999-2012 Gentoo Foundation# Distributed under the terms of the GNU General Public License v2# $Header: $EAPI=4DESCRIPTION="detox safely removes spaces and strange characters from filenames"HOMEPAGE="http://detox.sourceforge.net/"SRC_URI="mirror://sourceforge/${PN}/${P}.tar.bz2"LICENSE="BSD"SLOT="0"KEYWORDS="~hppa ~mips sparc x86"RDEPEND="dev-libs/popt"DEPEND="${RDEPEND}
sys-devel/flex
sys-devel/bison"
src_configure() {
econf --with-popt
}
src_install() {
emake DESTDIR="${D}" install
dodoc README CHANGES
}
# Copyright 1999-2012 Gentoo Foundation# Distributed under the terms of the GNU General Public License v2# $Header: $
EAPI=4
inherit eutils
DESCRIPTION="detox safely removes spaces and strange characters from filenames"
HOMEPAGE="http://detox.sourceforge.net/"
SRC_URI="mirror://sourceforge/${PN}/${P}.tar.bz2"
LICENSE="BSD"
SLOT="0"
KEYWORDS="~hppa ~mips ~sparc ~x86"
RDEPEND="dev-libs/popt"
DEPEND="${RDEPEND}
sys-devel/flex
sys-devel/bison"src_prepare() {
epatch "${FILESDIR}"/${P}-destdir.patch \
"${FILESDIR}"/${P}-parallel_build.patch
}
src_configure() {
econf --with-popt
}
src_install() {
emake DESTDIR="${D}" install
dodoc README CHANGES
}
注意${FILESDIR}/${P}-destdir.patch指向detox-1.1.0-destdir.patch,这个文件在Portage树的files/子文件夹中。更大的补丁文件必须在你的开发者空间dev.gentoo.org而不是files/或镜像中——参见Gentoo Mirrors和Patching with epatch。
# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: $
EAPI=4
DESCRIPTION="GNU charset conversion library for libc which doesn't implement it"HOMEPAGE="http://www.gnu.org/software/libiconv/"SRC_URI="ftp://ftp.gnu.org/pub/gnu/libiconv/${P}.tar.gz"
LICENSE="LGPL-2.1"SLOT="0"KEYWORDS="~amd64 ~ppc ~sparc ~x86"IUSE="nls"
DEPEND="!sys-libs/glibc"
src_configure() {
econf $(use_enable nls)
}
src_install() {
emake DESTDIR="${D}" install
}
defmakeHTMLbox(body, fontsize, imagesize): """takes one long string of words and a width(px) then put them in an HTML box""" boxStr = """<div style=\"font-size: %spx;line-height: 100%s; width: %s;background-color: rgb(0, 0, 0);border: 1px grey solid;text-align: center; overflow: hidden;\">%s</div> """ return boxStr % (fontsize, '%', imagesize[0], body)
defmakeHTMLascii(body, color): """take words and , and create an HTML word """ #num = str(random.randint(0,255)) # return random color for every tags color = 'rgb(%s, %s, %s)' % color # get the html data wordStr = '<span style=\"color:%s;float:left;\">%s</span>' return wordStr % (color, body)
defi2m(im, fontsize): """turn an image into ascii like matrix""" im = im.convert('L') im = ImageOps.autocontrast(im) im.thumbnail((im.size[0] / fontsize, im.size[1] / fontsize)) string = '' colors = [(0, i, 0) for i in range(0, 256, 17)] words = '据说只有到了十五字才会有经验的' for y in range(im.size[1]): for x in range(im.size[0]): p = im.getpixel((x, y)) i = 14 while i >= 0: if p >= i * 17: s = makeHTMLascii(words[3 * i:3 * (i + 1)], colors[i]) break i -= 1 if x % im.size[0] == 0and y > 0: s = s + '<br/>' string = string + s return string
defi2a(im, fontsize): """turn an image into ascii with colors""" im = im.convert('RGB') im = ImageOps.autocontrast(im) im.thumbnail((im.size[0] / fontsize, im.size[1] / fontsize)) string = '' for y in range(im.size[1]): for x in range(im.size[0]): c = im.getpixel((x, y)) # print c s = makeHTMLascii('翻', c) if x % im.size[0] == 0and y > 0: s = s + '<br/>' string = string + s return string
defgetHTMLascii(filename, fontsize, style='matrix', outputfile='a.html', scale=1): """Got html ascii image""" im = Image.open(filename) size = (int(im.size[0] * scale), int(im.size[1] * scale)) im.thumbnail(size, Image.ANTIALIAS) if style == 'matrix': ascii = makeHTMLbox(i2m(im, fontsize), fontsize, im.size) elif style == 'ascii': ascii = i2a(im, fontsize) else: print"Just support ascii and matrix now, fall back to matrix" ascii = makeHTMLbox(i2m(im, fontsize), fontsize, im.size) with open(outputfile, 'wb') as f: f.write(ascii) return1
Update: Mon 25 Feb 2013 11:38:47 AM CST add classic style. More refer to github
Python是面向对象的?没有对象面向毛对象。
——Anonymous
Several weeks ago, I saw a poster of presidential campaign for Obama, in which Obama’s portrait was made up of many voter’s photos. It really attracted me, somedays later, I want to make one myself.
The completed code host here. It is much more functional than object-oriented…
Search the Internet
First of all, I searched the Google to find out how others achieve it, then I found many interesting implement and post on it.Along with them, there are pretty demos around.One of the demo of Foto-Mosaik-Edda striked me.It declaims as follows in their site:
The Chaos Mosaic Picture is a new form of photo mosaic which can, at present, only be created by Foto-Mosaik-Edda.
Uhm…Foto-Mosaic-Edda is an open-source project that really impressive.But it was an C# project. Linux users don’t like it however.I don’t like mono.
I searched other open-source implement on photomosaic. I get some simple programs only use gray photos, and some complex ones can make beautiful classic photomosaic(like metapixel, even chaos style which it calls collage style), But none has as beautiful demos as Foto-Mosaic-Edda.(metapixel really amazing, it is robust and quickly.)
However, I saw many enthusiastic people write one themselves, it really looks interesting for me. I’ve used PIL for processing images when I tried to decode captchas several days ago, so I believe with the help of PIL, someone can achieve photomosaic simply.
So I just read the documentation of PIL, then start my hack.
Write My Own PhotoMosaic Generator
It’s not hard, however, what you should do is clear and simple:
analyse the image to be made mosaic, get a dict in which position as key and color as value.
use a bunch of images to get a dict, in which image name as key and colors as value.
thumbnail bunches of images and paste it in the right position, so that the big image looks like it consists of many small one.
I’d like to got the chaos style, so some other requirements:
defadd_frame(image): '''Add frame for image.''' im = ImageOps.expand(image, border=int(0.01 * max(image.size)), fill=0xffffff) return im
defdrop_shadow(image, offset, border=0, shadow_color=0x444444): """Add shadows for image""" # Caclulate size fullWidth = image.size[0] + abs(offset[0]) + 2 * border fullHeight = image.size[1] + abs(offset[1]) + 2 * border # Create shadow, hardcode color shadow = Image.new('RGBA', (fullWidth, fullHeight), (0, 0, 0)) # Place the shadow, with required offset shadowLeft = border + max(offset[0], 0) # if <0, push the rest of the image right shadowTop = border + max(offset[1], 0) # if <0, push the rest of the image down shadow.paste(shadow_color, [shadowLeft, shadowTop, shadowLeft + image.size[0], shadowTop + image.size[1]]) shadow_mask = shadow.convert("L") # Paste the original image on top of the shadow imgLeft = border - min(offset[0], 0) # if the shadow offset was <0, push right imgTop = border - min(offset[1], 0) # if the shadow offset was <0, push down shadow.putalpha(shadow_mask) shadow.paste(image, (imgLeft, imgTop)) return shadow
Then a function to rotate images.
1 2 3 4 5 6
defrotate_image(image, degree): '''Rotate images for specific degree. Expand to show all''' if image.mode != 'RGBA': image = image.convert('RGBA') im = image.rotate(degree, expand=1) return im
‘RGBA’ mode is to support transparency. What’s matter here is that jpeg/jpg does not support transparency. So you can’t get transparency shadows and rotate pictures if you just use jpg/jpeg images.So, write a function to process images with jpg/jpeg format, transpose it into png.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
defprocess_image(filename, newname): '''convert image to png to support transparency''' if filename.split('.')[-1] != 'png': im = Image.open(filename) im.save(newname + '.png') print"processing image file %s" % filename return1
defprocess_directory(path): os.chdir(path) count = 1 for filename in os.listdir(path): ext = filename.split('.')[-1] if ext == 'jpeg'or ext == 'jpg': process_image(filename, str(count)) os.remove(filename) count += 1 return1
Really poor work… But it works for me: )
We have to thumnail bunches of images, It’s easy to thumbnail with PIL:
1 2 3 4
defthumbnail(im, size): """thumnail the image""" im.thumbnail(size, Image.ANTIALIAS) return im
Let’s have a fun with them. To get heaps of images randomly on the desktop, I hardcoded these parameters to get my photos work, you HAVE TO find yours:
1 2 3 4 5 6 7 8 9 10
# Just for fun defchao_image(path, size=(800, 800), thumbnail_size=(50, 50), shadow_offset=(10, 10), backgroud_color=0xffffff): image_all = Image.new('RGB', size, backgroud_color) for image in os.listdir(path): if image.split('.')[-1] == 'png': im = Image.open(image) degree = random.randint(-30, 30) im = thumbnail(rotate_image(drop_shadow(add_frame(im), shadow_offset), degree), thumbnail_size) image_all.paste(im, (random.randint(-thumbnail_size[0], size[0]), random.randint(-thumbnail_size[1], size[1])), im) return image_all
Calculate Images And Compare
Get average colors of an image
1 2 3 4
defaverage_image(im): """return average (r,g,b) for image""" color_vector = [int(x) for x in ImageStat.Stat(im).mean] return color_vector
to compare images? Compare the (r,g,b) value of them.
1 2 3 4 5 6 7 8 9
defcompare_vectors(v1, v2): """compare image1 and image2, return relations""" if len(v1) == len(v2): distance = 0 for i in xrange(len(v1)): distance += (v1[i] - v2[i]) ** 2 return distance else: print"vector not match in dimensions"
I just use distance in (R, G, B) space to calculate similarity, someone advice compare in other space, you can change it just like the example in PIL’s documentation:
1 2 3 4 5 6
# May not useful defrgb2xyz(im): """rgb to xyz""" rgb2xyz = (0.412453, 0.357580, 0.180423, 0, 0.212671, 0.715160, 0.072169, 0, 0.019334, 0.119193, 0.950227, 0) out = im.convert("RGB", rgb2xyz) return out
But I find many implements just use R,G,B, and it works well.
Next, get a dict of image in current path, in which filename as key, average (R,G,B) colors as value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
deftile_dict(path): """Return list of average (R,G,B) for image in this path as dict.""" dic = {} for image in os.listdir(path): if image.split('.')[-1] == 'png': try: im = Image.open(image) except: print"image file %s cannot open" % image continue if im.mode != 'RGB': im = im.convert('RGB') dic[image] = average_image(im) return dic
We don’t need to calculate every pixel of the large picture, just thumbnail it to get a nearest color of different regions.
1 2 3 4 5 6
defthumbnail_background(im, scale): """thumbnail backgroud image""" newsize = im.size[0] / scale, im.size[1] / scale im.thumbnail(newsize) print'thumbnail size and the number of tiles %d X %d' % im.size return im.size
For every pixel in the thumbnailed large image, find most similar small image filenames.(top ten):
1 2 3 4 5 6 7 8 9 10
deffind_similar(lst, dic): """for lst([R, G, B], Calculate which key-value in dic has the most similarity.Return first 10)""" similar = {} for k, v in dic.items(): similar[k] = compare_vectors(v, lst) # if len(v) != len(lst): # print v, len(v), lst, len(lst) similar = [(v, k) for k, v in similar.items()] # Not good, lost the same Score similar.sort() return similar[:10]
Poor hack, but it really works…
Final Work
Now it’s the final magic.
Get the small image in order, the order imply where it should be. Then rotate, add shadows and frames for small images, finally paste it onto the large one randomly in the right position:
defget_image_list(im, dic): """receive a thumbnail image and a dict of image to be mosaic, return tiles(filename) in order(as a list)""" lst = list(im.getdata()) tiles = [] for i in range(len(lst)): #print find_similar(lst[i], dic)[random.randrange(10)][1] tiles.append(find_similar(lst[i], dic)[random.randrange(10)][1]) return tiles defpaste_chaos(image, tiles, size, shadow_off_set=(30, 30)): """size is thumbnail of backgroud size that is how many tiles per line and row""" # image_all = Image.new('RGB', image.size, 0xffffff) image_all = image lst = range(len(tiles)) random.shuffle(lst) fragment_size = (image.size[0] / size[0], image.size[1] / size[1]) print'tiles size %d X %d' % fragment_size print'number of tiles one iteration: %d' % len(lst) for i in lst: im = Image.open(tiles[i]) degree = random.randint(-20, 20) im = thumbnail(rotate_image(drop_shadow(add_frame(im), shadow_off_set), degree), (fragment_size[0] * 3 / 2, fragment_size[1] * 3 / 2)) x = i % size[0] * fragment_size[0] + random.randrange(-fragment_size[0] / 2, fragment_size[0] / 2) y = i / size[0] * fragment_size[1] + random.randrange(-fragment_size[1] / 2, fragment_size[1] / 2) # print x, y image_all.paste(im, (x, y), im) return image_all
I try it like this, I know parameter n is tricky, it was the scale it thumbnail the large image. Maybe I’ll change it to something more clear later…
defmain(filename, n, scale, iteration, path='./'): # 0. select an big image for mosaic print"open %s" % filename im = Image.open(filename) # 1. process image as png to support transparency print"process directory %s" % path process_directory(path) # 2. get a dict for path print"get tile dict for path `%s`" % path try: with open('dic.txt', 'r') as f: dic = p.load(f) except: dic = tile_dict(path) with open('dic.txt', 'wb') as f: p.dump(dic, f) # 3. thumbnail the big image for compare print"thumbnail background for compare" # n = 30 # 原始图片缩为多少分之一 # scale = 3 # 原始图片放大倍数 big_size = im.size[0] * scale, im.size[1] * scale im_chao = Image.new('RGB', big_size, 0xffffff) imb_t_size = thumbnail_background(im, n) print"how may tiles: %d X %d" % imb_t_size print'number of iterations: %d' % iteration for i in range(iteration): print'iteration: %d' % (i + 1) # 4. get a list of smail image for mosaic print"get pic list" im_tiles = get_image_list(im, dic) # 5. paste in chaos style print"generate final image" im_chao = paste_chaos(im_chao, im_tiles, imb_t_size) return im_chao
if __name__ == '__main__': im = main('../mm.jpg', 15, 5, 2) im.save('../final3.png') im.show()
deflogin(username, password): """log in and return uid""" logpage = "http://www.renren.com/ajaxLogin/login" data = {'email': username, 'password': password} login_data = urllib.urlencode(data) cj = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) urllib2.install_opener(opener) res = opener.open(logpage, login_data) print"Login now ..." html = res.read() #print html
# Get uid print"Getting user id of you now" res = urllib2.urlopen("http://www.renren.com/home") html = res.read() # print html uid = re.search("'ruid':'(\d+)'", html).group(1) # print uid print"Login and got uid successfully" return uid
m = re.findall(pattern, html) #print len(m) if len(m) == 0: break for i in range(0, len(m)): no = m[i][0] uname = m[i][1] #print uname, no dict1[no] = uname pagenum += 1 print"Got %s 's friends list successfully." % str(uid) return dict1
我们再写个获取好友关系字典的函数,为了避免我们每次为了获取字典都要登录抓取。
1 2 3 4 5 6 7 8 9 10
defgetdict(uid): """cache dict of uid in the disk.""" try: with open(str(uid) + '.txt', 'r') as f: dict_uid = p.load(f) except: with open(str(uid) + '.txt', 'w') as f: p.dump(getfriends(uid), f) dict_uid = getdict(uid) return dict_uid
我们还需要一个用来判断两个人关系的函数,来判断我们好友之间的关系。
1 2 3 4 5 6 7
defgetrelations(uid1, uid2): """receive two user id, If they are friends, return 1, otherwise 0.""" dict_uid1 = getdict(uid1) if uid2 in dict_uid1: return1 else: return0
defgetgraph(username, password): """Get the Graph Object and return it. You must specify a Chinese font such as `SimHei` in ~/.matplotlib/matplotlibrc""" uid = login(username, password) dict_root = getdict(uid) # Get root tree
G = nx.Graph() # Create a Graph object for uid1, uname1 in dict_root.items(): # Encode Chinese characters for matplotlib **IMPORTANT** # if you want to draw Chinese labels, uname1 = unicode(uname1, 'utf8') G.add_node(uname1) for uid2, uname2 in dict_root.items(): uname2 = unicode(uname2, 'utf8') # Not necessary for networkx if uid2 == uid1: continue if getrelations(uid1, uid2): G.add_edge(uname1, uname2)
defdraw_graph(username, password, filename='graph.txt', label_flag=True, remove_isolated=True, different_size=True, iso_level=10, node_size=40): """Reading data from file and draw the graph.If not exists, create the file and re-scratch data from net""" print"Generating graph..." try: with open(filename, 'r') as f: G = p.load(f) except: G = getgraph(username, password) with open(filename, 'w') as f: p.dump(G, f) #nx.draw(G) # Judge whether remove the isolated point from graph if remove_isolated isTrue: H = nx.empty_graph() for SG in nx.connected_component_subgraphs(G): if SG.number_of_nodes() > iso_level: H = nx.union(SG, H) G = H # Ajust graph for better presentation if different_size isTrue: L = nx.degree(G) G.dot_size = {} for k, v in L.items(): G.dot_size[k] = v node_size = [G.dot_size[v] * 10for v in G] pos = nx.spring_layout(G, iterations=50) nx.draw_networkx_edges(G, pos, alpha=0.2) nx.draw_networkx_nodes(G, pos, node_size=node_size, node_color='r', alpha=0.3) # Judge whether shows label if label_flag isTrue: nx.draw_networkx_labels(G, pos, alpha=0.5) #nx.draw_graphviz(G) plt.show()
defcmd2string(filename):'''accept the filename and return the string of cmd'''
chist = []
# Open the file and store the history in chistwith open(filename, 'r') as f:
chist = f.readlines()
# print chistfor i in range(len(chist)):
chist[i] = chist[i].split()
chist[i] = chist[i][1]
ss = ''for w in chist:
if w != 'sudo'and w != 'pacman':
ss = ss + ' ' + w
return ss
接着将字符串转换成字典,单词为键,出现次数为值:
defstring2dict(string, dic):"""split a string into a dict record its frequent"""
wl = string.split()
for w in wl:
if w == '\n': # 因为后来我看到中文分词结果中有\n...continueif w notin dic:
dic[w] = 1else:
dic[w] += 1return dic
defmakeHTMLbox(body, width):"""takes one long string of words and a width(px) then put them in an HTML box"""
boxStr = """<div style=\"width: %spx;background-color: rgb(0, 0, 0);border: 1px grey solid;text-align: center; overflow: hidden;\">%s</div>
"""return boxStr % (str(width), body)
defmakeHTMLword(body, fontsize):"""take words and fontsize, and create an HTML word in that fontsize."""#num = str(random.randint(0,255))# return random color for every tags
color = 'rgb(%s, %s, %s)' % (str(random.randint(0, 255)), str(random.randint(0, 255)), str(random.randint(0, 255)))
# get the html data
wordStr = '<span style=\"font-size:%spx;color:%s;float:left;\">%s</span>'return wordStr % (str(fontsize), color, body)
Now, it’s time to get the proper html files of the tag cloud!
# get the html data first
wd = {}
s = cmd2string(filename)
wd = string2dict(s, wd)
vkl = [(k, v) for k, v in wd.items() if v >= omitnumber andlen(k) > omitlen] # kick off less used cmdwords = ""for w, c in vkl:
words += makeHTMLword(w, int(c * fontScale + basescale))
html = makeHTMLbox(words, boxsize)
# dump it to a filewithopen(filename.split('.')[0] + '.' + 'html', 'wb') as f:
f.write(html)
将以上内容写到一个文件中,命名为比如说tagcloud.py:
python2 tagcloud.py hist.txt # `import sys` and let filename = sys.argv[1]
# or`run tagcloud.py hist.txt` in ipython
import jieba
withopen('shibada.txt','r') as f:
s = f.read()
wg = jieba.cut(s, cut_all=True)
wd = {}
for w in wg:
if w notin wd:
wd[w] = 1else:
wd[w] += 1
生成html数据:
for w, c in vkl:
words += makeHTMLword(w, int(c * fontScale + basescale))
html = makeHTMLbox(words, boxsize)
htmlzh = unicode.encode(html,'UTF-8') # Important!# dump it to a filewithopen(filename.split('.')[0] + '.' + 'html', 'wb') as f:
f.write(htmlzh)
True story: I needed to send a file containing several millions of keys to a coworker.(The company did not work on Unix.) Since the file was too large to fit safely into an email message, I posted it to a web server on my desktop and sent my coworker the link. (I dutifully had provided the file with the extension .txt, so that he would be able to open it.) Five minutes later, he calls me back: “I can’t open that”—“What do you mean?”—“Well, I click the link, but ScrapPaper [the default text editor for small text files on this particular system] dies because the file is too big.” This coworker was not inept (in fact, he was quite good at his primary job), but he displayed the particular non-problem-solving attitude that develops in predefined work environments: “Link, click.” It did not even occur to him to think of something else to try. That’s a problem!
Unix was developed for precisely this kind of ad hoc programming with files and data, and it continues to provide the most liberating environment for such work. Unix (and its variants, including Linux and Mac OS X) has some obvious technical advantages, but its most important property in the present context is that it encourages you to devise solutions. It does not try (or pretend) to do the job for you, but it goes out of its way to give you tools that you might find handy—without prescribing how or for what you use them. In contrast, other operating systems tend to encourage you to stay within the boundaries of certain familiar activity patterns—which does not encourage the development of your problem-solving abilities (or, more importantly, your problem-solving attitudes).
关键就在这里,Unix系统 encourages you to devise solutions, 它不尝试为你做完所有事,但是它竭尽所能为你提供各种各样的工具和选择,而不是规定使用的方式和用途。而其它操作系统往往鼓励你留在习惯的活动模式范围内——毫不鼓励你提高解决问题的能力,更重要的是,解决问题的态度!